/*
 * Copyright 2012 Gregor Schauer
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package at.schauer.gregor.vienna.serializer;

import at.schauer.gregor.vienna.exception.ViennaException;
import at.schauer.gregor.vienna.serializer.builder.ObjectBuilder;
import at.schauer.gregor.vienna.serializer.decomposer.ObjectDecomposer;
import at.schauer.gregor.vienna.serializer.plugin.SerializerPlugin;
import at.schauer.gregor.vienna.serializer.populator.ObjectPopulator;
import at.schauer.gregor.vienna.util.ViennaUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.ObjectUtils;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Gregor Schauer
 */
public class DefaultSerializer<T> implements Serializer<T> {
	protected Map<Class<?>, List<SerializerPlugin>> pluginMap = new HashMap<Class<?>, List<SerializerPlugin>>();
	protected Map<Class<?>, ObjectBuilder<? extends T>> builderMap = new HashMap<Class<?>, ObjectBuilder<? extends T>>();
	protected Map<Class<?>, ObjectPopulator<? extends T>> populatorMap = new HashMap<Class<?>, ObjectPopulator<? extends T>>();
	protected Map<Class<?>, ObjectDecomposer<? extends T>> decomposerMap = new HashMap<Class<?>, ObjectDecomposer<? extends T>>();

	@Override
	@SuppressWarnings("unchecked")
	public <E extends T> E load(Map<String, Object> map) {
		try {
			Class<E> clazz = (Class<E>) ClassUtils.getClass(String.valueOf(map.remove(CLASS)));
			return load(map, clazz);
		} catch (ClassNotFoundException e) {
			throw new ViennaException(e);
		}
	}

	@Override
	@SuppressWarnings("unchecked")
	public <E extends T> E load(Map<String, Object> map, Class<E> clazz) {
		map.remove(CLASS);
		List<SerializerPlugin> plugins = ObjectUtils.defaultIfNull(ViennaUtils.filter(pluginMap, clazz), Collections.<SerializerPlugin>emptyList());
		for (SerializerPlugin<T> plugin : plugins) {
			map = plugin.preConstruct(map, clazz);
		}
		E obj = construct(map, clazz);
		for (SerializerPlugin<T> plugin : plugins) {
			obj = plugin.postConstruct(map, obj);
		}
		obj = initialize(map, obj);
		for (SerializerPlugin<T> plugin : plugins) {
			obj = plugin.postInitialize(map, obj);
		}
		return obj;
	}

	@SuppressWarnings("unchecked")
	protected <E extends T> E construct(Map<String, Object> map, Class<E> clazz) {
		ObjectBuilder<E> builder = (ObjectBuilder<E>) ViennaUtils.filter(builderMap, clazz);
		return builder.construct(map, clazz);
	}

	@SuppressWarnings("unchecked")
	protected <E extends T> E initialize(Map<String, Object> map, E obj) {
		ObjectPopulator<E> populator = (ObjectPopulator<E>) ViennaUtils.filter(populatorMap, obj.getClass());
		return populator.initialize(map, obj);
	}

	@Override
	@SuppressWarnings("unchecked")
	public Map<String, Object> save(T obj) {
		Map<String, Object> map = new HashMap<String, Object>();
		List<SerializerPlugin> plugins = ObjectUtils.defaultIfNull(ViennaUtils.filter(pluginMap, obj.getClass()), Collections.<SerializerPlugin>emptyList());
		for (SerializerPlugin<T> plugin : plugins) {
			map = plugin.preDeconstruct(map, obj);
		}
		map = deconstruct(map, obj);
		for (SerializerPlugin<T> plugin : plugins) {
			map = plugin.postDeconstruct(map, obj);
		}
		map = finalize(map, obj);
		for (SerializerPlugin plugin : plugins) {
			map = plugin.postFinalize(map, obj);
		}
		return map;
	}

	@SuppressWarnings("unchecked")
	protected <E extends T> Map<String, Object> deconstruct(Map<String, Object> map, E obj) {
		ObjectDecomposer<E> decomposer = (ObjectDecomposer<E>) ViennaUtils.filter(decomposerMap, obj.getClass());
		return decomposer.deconstruct(map, obj);
	}

	protected <E extends T> Map<String, Object> finalize(Map<String, Object> map, E obj) {
		map.put("class", obj.getClass().getName());
		return map;
	}

	public Map<Class<?>, List<SerializerPlugin>> getPluginMap() {
		return pluginMap;
	}

	public void setPluginMap(Map<Class<?>, List<SerializerPlugin>> pluginMap) {
		this.pluginMap = pluginMap;
	}

	public Map<Class<?>, ObjectBuilder<? extends T>> getBuilderMap() {
		return builderMap;
	}

	public void setBuilderMap(Map<Class<?>, ObjectBuilder<? extends T>> builderMap) {
		this.builderMap = builderMap;
	}

	public Map<Class<?>, ObjectPopulator<? extends T>> getPopulatorMap() {
		return populatorMap;
	}

	public void setPopulatorMap(Map<Class<?>, ObjectPopulator<? extends T>> populatorMap) {
		this.populatorMap = populatorMap;
	}

	public Map<Class<?>, ObjectDecomposer<? extends T>> getDecomposerMap() {
		return decomposerMap;
	}

	public void setDecomposerMap(Map<Class<?>, ObjectDecomposer<? extends T>> decomposerMap) {
		this.decomposerMap = decomposerMap;
	}
}
